/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

//#ifndef __FILESYSTEM_STRING_H__
//#define __FILESYSTEM_STRING_H__

#pragma once

#include <Converter.h>
#include <StaticRefCounted.h>
#include <map>
#include <string>

namespace liba
{
namespace filesystem
{
	/** This class incapsulates operating system differences for file and path names.
	Two PathStrings, that refer to the same OS path should compare equally.
	filesystem::String can be constructed from ANSI and Unicode strings.
	The return type for {@link #c_str}, {@link #data}, {@link #begin}, {@link #end}
	is dependent on OS used. This methods should be used only by OS-dependent code. */

	//filesystem::String recognizes the following templates which may appear at path beginning:
	//	%app_path%												- path to the folder, in which the main process's module file is located (for dlls, the %app_path% instantiates into path to the .exe-file which has loaded that dll.
	//  %app_name%											- path to the main process's module file 
	//	%system_path%										- path to winnt/system32 (path to all shared dlls)
	//	%windows_path%										- path to winnt or windows directory
	//	%desktop_path%										- path to the desktop directory
	//	%fonts_path%											- path to system fonts directory
	//	%start_menu_path%									- path to start menu folder
	//	%start_menu_programs_path%					- path to start menu/programs folder
	//	%common_desktop_path%							- path to the desktop directory
	//	%common_start_menu_path%					- path to start menu folder
	//	%common_start_menu_programs_path%		- path to start menu/programs folder
	//  %local_appdata% - local application data for iPhone (writeable, "Documents" folder)
	class StringStatics
	{
		typedef std::map<std::string, std::string> Templates;
		Templates templates;
	public:
		StringStatics();
		~StringStatics()
		{}

		void add_path_instantiation(const std::string & from, const std::string & to);
		std::string trim_file_name(const std::string & str);
		std::string trim_extension(const std::string & str);
		std::string get_file_name(const std::string & str)const;
		std::string get_extension(const std::string & str)const;
		std::string normalize(const std::string & str);
	};

	class String : protected StaticRefCounted<StringStatics>
	{
		typedef std::string RepType;
		RepType rep;
		
#ifdef IPHONE_VERSION
		mutable RepType rep_for_comparison;	//tolowered copy for case sensitive filesystems
#endif
		//This method transforms filesystem path string to it's normal form:
		//	- changes upper-case to lower-case;
		//	- replaces "\" with "/";
		//	- replaces templates with instantiated values (if them found at the beginning).
		void normalize();
		static size_t find_next_slash(const String * s, size_t p1 = 0);
	public:
		static void add_path_instantiation(const std::string & from, const std::string & to);

		//author thought that this class is useful, but others not :)
		class iterator
		{
			const String * parent;
			size_t p1, p2;
		public:
			iterator(const String * parent, size_t p1, size_t p2)
			:	parent(parent),
				p1(p1),
				p2(p2)
			{}
			String operator*()const
			{
				return parent->rep.substr(p1, p2 - p1).c_str();
			}
			bool operator==(const iterator & i)const
			{
				return i.p1 == p1 && i.p2 == p2;	
			}
			bool operator!=(const iterator & i)const
			{
				return !operator==(i);
			}
			iterator & operator++();
			iterator operator++(int)
			{
				iterator tmp(*this);
				++*this;
				return tmp;
			}
		};

		friend class iterator;

		iterator begin()const;
		iterator end()const	
		{
			return iterator( this, std::string::npos, std::string::npos );
		}

		String trim_file_name()const;
		String trim_extension()const;
		String get_file_name()const;
		String get_extension()const;

		String()
		{}

		String(const std::string & str)
		{
			assign( str.c_str(), str.c_str() + str.size() );
		}

		String(const std::wstring & str)
		{
			assign( str.c_str(), str.c_str() + str.size() );
		}
		String(const char * str);
		String(const wchar_t * str);
		String(const char * be, const char * en)
		{
			assign( be, en );
		}
		String(const wchar_t * be, const wchar_t * en)
		{
			assign( be, en );
		}
		const String & assign(const char * begin, const char * end);
		const String & assign(const wchar_t * begin, const wchar_t * end);
		const String & operator+=(const String & str);
		const char * c_str()const
		{
			return rep.c_str();
		}
		size_t size()const
		{
			return rep.size();
		}
		bool empty()const
		{
			return rep.empty();
		}
		int compare(const String & str)const
		{
#ifdef IPHONE_VERSION
			return rep_for_comparison.compare(str.rep_for_comparison);
#else
			return rep.compare( str.rep );
#endif
		}

		template<typename Sym>
		struct ConverterBuffer
		{
			bool convert(const filesystem::String & value)
			{
				return converter::convert( value.rep, &data );
			}
			const Sym * begin()const
			{
				return data.c_str();
			}
			const Sym * end()const
			{
				return data.c_str() + data.size();
			}
		private:
			std::basic_string<Sym> data;
		};

		template<class Sym>
		bool convert(const Sym * be, const Sym * en)
		{
			if( !converter::convert( std::basic_string<Sym>(be, en), &rep ) )
				return false;

			normalize();

			return true;
		}
	};

	inline String::String(const char * str)
	:	rep(str)
	{
		normalize();
	}

	inline String::String(const wchar_t * str)
	{
		std::wstring wstr( str );
		converter::convert( wstr, &rep );
		normalize();
	}

	inline bool operator==(const String & a, const String & b)
	{
		return a.compare( b ) == 0;
	}
	inline bool operator!=(const String & a, const String & b)
	{
		return !operator==( a, b );
	}
	inline bool operator<(const String & a, const String & b)
	{
		return a.compare( b ) < 0;
	}
	inline String operator+(const String & a, const String & b)
	{
		return String( a ) += b;
	}
	
	inline const String & String::assign(const char * begin, const char * end)
	{
		rep.assign(begin, end);
		normalize();
		return *this;
	}

	inline const String & String::assign(const wchar_t * begin, const wchar_t * end)
	{
		std::wstring ws(begin, end);
		converter::convert(ws, &rep);
		normalize();
		return *this;
	}

	inline const String & String::operator+=(const String & str)
	{
		rep += str.rep;
		normalize();
		return *this;
	}

	/** Appends one path to another adding slash between them if necessary */
	String combine(const String& left, const String& right);

} // namespace filesystem

namespace converter
{
	template<>
	struct ConverterBestCharSelector< filesystem::String >
	{
		typedef char BestChar;
	};

} // namespace converter
} // namespace liba
using namespace liba;

//#endif //__FILESYSTEM_STRING_H__